"""
用python实现一个渲染器
"""
import io
import numpy as np
import math
"""
{
    "points": -> ndarray[n,4]
    "trangles": -> ndarray[n,6] or ndarray[n,3]
    "textures" : -> ndarray[n,3]
    "material": [(材质坐标,材质数组,材质类型),...]
}
"""

def build_quad(w:float,h:float)->dict:
    """
    创建一个矩形面
    """
    pts = np.ndarray([4,4],dtype=np.float32)
    trs = np.ndarray([2,3],dtype=np.int32)
    txs = np.ndarray([4,3],dtype=np.float32)
    pts[0] = (-w/2,h/2,0,1)
    pts[1] = (w/2,h/2,0,1)
    pts[2] = (w/2,-h/2,0,1)
    pts[3] = (-w/2,-h/2,0,1)
    trs[0] = (0,1,2)
    trs[1] = (0,2,3)
    txs[0] = (0,0,1)
    txs[1] = (0,1,1)
    txs[2] = (1,1,1)
    txs[3] = (1,0,1)
    return {"points":pts,
            "trangles":trs,
            "textures":txs,
            "type":"quad",
            "meta":(w,h)}

def build_box(w:float,h:float,l:float)->dict:
    """
    构造一个盒子
    """
    pts = np.ndarray([8,4],dtype=np.float32)
    trs = np.ndarray([12,6],dtype=np.int32)
    txs = np.ndarray([14,3],dtype=np.float32)
    pts[0] = (-w/2,h/2,l/2,1)
    pts[1] = (w/2,h/2,l/2,1)
    pts[2] = (w/2,h/2,-l/2,1)
    pts[3] = (-w/2,h/2,-l/2,1)
    pts[4] = (-w/2,-h/2,l/2,1)
    pts[5] = (w/2,-h/2,l/2,1)
    pts[6] = (w/2,-h/2,-l/2,1)
    pts[7] = (-w/2,-h/2,-l/2,1)
    trs[0] = (0,1,5,0,1,4) #前三个是三角顶点索引，后三个是对应的纹理索引
    trs[1] = (0,5,4,0,4,3)
    trs[2] = (0,4,7,2,3,7)
    trs[3] = (0,7,3,2,7,6)
    trs[4] = (4,5,6,3,4,8)
    trs[5] = (4,6,7,3,8,7)
    trs[6] = (5,1,2,4,5,9)
    trs[7] = (5,2,6,4,9,8)
    trs[8] = (7,6,2,7,8,11)
    trs[9] = (7,2,3,7,11,10)
    trs[10] = (1,0,3,10,11,13)
    trs[11] = (1,3,2,10,13,12)
    txs[0] = (1/3,1,1)
    txs[1] = (2/3,1,1)
    txs[2] = (0,3/4,1)
    txs[3] = (1/3,3/4,1)
    txs[4] = (2/3,3/4,1)
    txs[5] = (1,3/4,1)
    txs[6] = (0,1/2,1)
    txs[7] = (1/3,1/2,1)
    txs[8] = (2/3,1/2,1)
    txs[9] = (1,1/2,1)
    txs[10] = (1/3,1/4,1)
    txs[11] = (2/3,1/4,1)
    txs[12] = (1/3,0,1)
    txs[13] = (2/3,0,1)
    return {"points":pts,
            "trangles":trs,
            "textures":txs,
            "type":"box",
            "meta":(w,h,l)}

def build_sphere(r:float,x_sub:int=12,y_sub:int=6)->dict:
    """
    构造一个球面
    x_sub为经度的细分程度 y_sub为纬度的细分程度
    a纬度 b经度
    """
    pts = np.ndarray([x_sub*y_sub,4],dtype=np.float32)
    trs = np.ndarray([x_sub*y_sub*2-2*x_sub,3],dtype=np.int32)
    txs = np.ndarray([x_sub*y_sub,3],dtype=np.float32)
    pi = math.pi
    t = 0
    for y in range(0,y_sub):
        for x in range(0,x_sub):
            x_pos = math.cos(x/(x_sub-1)*2*pi)*math.sin(y/(y_sub-1)*pi)*r
            y_pos = math.cos(y/(y_sub-1)*pi)*r
            z_pos = math.sin(x/(x_sub-1)*2*pi)*math.sin(y/(y_sub-1)*pi)*r
            pts[t] = (x_pos,y_pos,z_pos,1)
            txs[t] = (x/(x_sub-1),y/(y_sub-1),1)
            t += 1
    for i in range(0,x_sub):
        trs[i] = (0,x_sub+i+1,x_sub+i)       
    t = x_sub
    for y in range(1,y_sub-1):
        for x in range(0,x_sub):
            trs[t] = (y*x_sub+x,y*x_sub+x+1,(y+1)*x_sub+x)         
            trs[t+1] = (y*x_sub+x,(y+1)*x_sub+x,(y+1)*x_sub+x-1)
            t += 2        
    #t = x_sub + 2*(y_sub-2)*x_sub + x_sub
    #t = 2*x_sub*y_sub-2*x_sub
    ei = x_sub*y_sub-1
    for i in range(0,x_sub):
        trs[t] = (ei,(y_sub-2)*x_sub+i,(y_sub-2)*x_sub+i+1)
        t += 1
    return {"points":pts,
            "trangles":trs,
            "textures":txs,
            "type":"sphere",
            "meta":(r,x_sub,y_sub)}

def build_cylinder(r,h):
    """
    构造柱面
    """
    pass

def build_obj(filename):
    """
    加载.obj文件
    """
    pt_ls = []
    tr_ls = []
    tx_ls = []
    no_ls = []
    rr = 0.
    with io.open(filename,"r",encoding="utf-8") as f:
        for line in f.readlines():
            ls = line[:-1].split(' ')
            if len(ls)==4 and ls[0] == 'v': #顶点
                x,y,z = float(ls[1]),float(ls[2]),float(ls[3])
                rr = max(rr,x*x+y*y+z*z)
                pt_ls.append((x,y,z,1.))
            elif len(ls)==4 and ls[0] == 'vn': #法线方向
                x,y,z = float(ls[1]),float(ls[2]),float(ls[3])
                no_ls.append((x,y,z,1.))
            elif len(ls)==3 and ls[0] == 'vt':
                tx_ls.append((float(ls[1]),float(ls[2]),1.))
            elif (len(ls)==5 or len(ls)==4) and ls[0] == 'f':
                f = []
                for i in range(1,len(ls)):
                    s = ls[i].split('/')
                    f.append((int(s[0])-1,int(s[1])-1,int(s[2])-1))
                if len(f)==3:
                    #tr_ls.append((f[0][0],f[1][0],f[2][0],f[0][1],f[1][1],f[2][1],f[0][2],f[1][2],f[2][2]))
                    tr_ls.append((f[2][0],f[1][0],f[0][0],f[2][1],f[1][1],f[0][1],f[2][2],f[1][2],f[0][2]))
                else:
                    #tr_ls.append((f[0][0],f[1][0],f[2][0],f[0][1],f[1][1],f[2][1],f[0][2],f[1][2],f[2][2]))
                    #tr_ls.append((f[2][0],f[3][0],f[0][0],f[2][1],f[3][1],f[0][1],f[2][2],f[3][2],f[0][2]))
                    tr_ls.append((f[2][0],f[1][0],f[0][0],f[2][1],f[1][1],f[0][1],f[2][2],f[1][2],f[0][2]))
                    tr_ls.append((f[0][0],f[3][0],f[2][0],f[0][1],f[3][1],f[2][1],f[0][2],f[3][2],f[2][2]))

    return {"points":np.array(pt_ls,dtype=np.float32),
            "trangles":np.array(tr_ls,dtype=np.int32),
            "textures":np.array(tx_ls,dtype=np.float32),
            "normales":np.array(no_ls,dtype=np.float32),
            "type":"mesh",
            "meta": (filename,math.sqrt(rr))}
